#include "CAplicacion.hpp"

#include <stdio.h>
#include <sys/timeb.h>
#include <time.h>

/*
Inicializamos los atributos estaticos
*/
CAplicacion * CAplicacion::singleton=NULL;

CAplicacion::CAplicacion()
{
	singleton = this;

	/*
	//PhysX
	NxPhysicsSDKDesc desc;
	NxSDKCreateError errorCode = NXCE_NO_ERROR;

	ErrorStream *es = new ErrorStream();

	gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL, es , desc, &errorCode);

	if(gPhysicsSDK == NULL)
	{
		cerr << "Tienes el PhysX v2.8.1 ?" << endl;
		salir(0);
	}

	gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05f);
	//gPhysicsSDK->setParameter(NX_ADAPTIVE_FORCE, 0);

	// Create a scene
	NxSceneDesc sceneDesc;
	//sceneDesc.flags |= NX_SF_SIMULATE_SEPARATE_THREAD;
	//sceneDesc.flags |= NX_SF_ENABLE_ACTIVETRANSFORMS;
	sceneDesc.gravity = NxVec3(0.0f, -98.8f, 0.0f);

	gScene = gPhysicsSDK->createScene(sceneDesc);
	if(gScene == NULL)
	{
		cerr << "No se crear la escena" << endl;
		salir(1);
	}

	// Establecer material por defecto
	NxMaterial * defectoMaterial = gScene->getMaterialFromIndex(0);
	defectoMaterial->setRestitution(0.5f);
	defectoMaterial->setStaticFriction(1.0f);
	defectoMaterial->setDynamicFriction(0.5f);
	//*/



	ErrorStream	gErrorStream;
	NxPhysicsSDKDesc desc;
	NxSDKCreateError errorCode = NXCE_NO_ERROR;
	gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL, &gErrorStream, desc, &errorCode);
	
	if(gPhysicsSDK == NULL) 
	{
		printf("\nSDK create error (%d - %s).\nUnable to initialize the PhysX SDK, exiting the sample.\n\n", errorCode, getNxSDKCreateError(errorCode));
		salir(0);
	}

	gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05f); //0.00000025f);

	/*
	gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1.0f);	//enable visualisation

	gPhysicsSDK->setParameter(NX_VISUALIZE_BODY_AXES, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_AXES, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_JOINT_LOCAL_AXES, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_JOINT_WORLD_AXES, 1.0f);
	gPhysicsSDK->setParameter(NX_VISUALIZE_JOINT_LIMITS, 1.0f);
	*/

	// Don't slow down jointed objects
	gPhysicsSDK->setParameter(NX_ADAPTIVE_FORCE, 0.0f);
	
	NxUserContactReport*	carContactReport	= new VehicleContactReport();
	
	NxSceneDesc sceneDesc;
	sceneDesc.gravity				= NxVec3(0.0f, -9.81f, 0.0f);
	sceneDesc.userContactReport		= carContactReport;
	
	gScene = gPhysicsSDK->createScene(sceneDesc);
	
	if(gScene == NULL)
	{
		printf("\nError: Unable to create a PhysX scene.\n\n");
		salir(1);
	}

	NxMaterial * defaultMaterial = gScene->getMaterialFromIndex(0); 
	defaultMaterial->setRestitution(0.0f);
	defaultMaterial->setStaticFriction(0.8f);
	defaultMaterial->setDynamicFriction(0.8f);

	


	//*********************

	wiimoteController = new WiimoteController();

	bool connectionResult = wiimoteController->connect();

	if(!connectionResult)
	{
		Beep(1000,100);
	}

	//*********************

	mSoundManager = new OgreOggSoundManager();
}

const char* CAplicacion::getNxSDKCreateError(const NxSDKCreateError& errorCode)
{
	switch(errorCode)
	{
		case NXCE_NO_ERROR: return "NXCE_NO_ERROR";
		case NXCE_PHYSX_NOT_FOUND: return "NXCE_PHYSX_NOT_FOUND";
		case NXCE_WRONG_VERSION: return "NXCE_WRONG_VERSION";
		case NXCE_DESCRIPTOR_INVALID: return "NXCE_DESCRIPTOR_INVALID";
		case NXCE_CONNECTION_ERROR: return "NXCE_CONNECTION_ERROR";
		case NXCE_RESET_ERROR: return "NXCE_RESET_ERROR";
		case NXCE_IN_USE_ERROR: return "NXCE_IN_USE_ERROR";
		default: return "Unknown error";
	}
};

CAplicacion::~CAplicacion()
{
	salir( 0 );
}

void CAplicacion::salir( int retorno )
{
	if(gPhysicsSDK != NULL)
	{
		if(gScene != NULL) gPhysicsSDK->releaseScene(*gScene);
		gScene = NULL;
		NxReleasePhysicsSDK(gPhysicsSDK);
		gPhysicsSDK = NULL;

	}
	exit( retorno );
}

NxPhysicsSDK * CAplicacion::getNxPhysicsSDK()
{
	return gPhysicsSDK;
}

NxScene * CAplicacion::getNxScene()
{
	return gScene;
}

SceneManager * CAplicacion::getSceneManager()
{
	return mSceneMgr;
}

Root * CAplicacion::getRoot()
{
	return mRoot;
}

RenderWindow * CAplicacion::getRenderWindow()
{
	return mWindow;
}

Camera * CAplicacion::getCamera()
{
	return mCamera;
}

void CAplicacion::createScene(void)
{
	
    CEntidad * cubo1 = new CEntidad(Vector3(5,30,5));
    cubo1->anadirFormaCubo("cubo1" , Corcho , 0.5,0.5,0.5, NULL, 
		"Examples/B" //"Examples/RustySteel"
	);
	//*
	CEntidad * cubo2 = new CEntidad(Vector3(0,10,-2));
    cubo2->anadirFormaCubo("cubo2" , Corcho , 1.5,1.5,0.5, NULL, 
		"Examples/B" //"Examples/Rocky"
	);

	CEntidad * cubo3 = new CEntidad(Vector3(1,10,2));
    cubo3->anadirFormaCubo("cubo3" , MaderaPocoDensa , 0.35,2.5,0.35, NULL, 
		"Examples/B" //"Examples/DarkMaterial"
	); 
	//*/

	CVehicleEntity* vehicleE = new CVehicleEntity( SPORTCAR, "V1", NxVec3(0.0f, 0.0f, 0.0f), 180.0f );

    CEntidad * suelo1 = new CEntidad(Vector3(-150,0,-150));
    suelo1->anadirFormaSuelo("suelo1", MaderaPocoDensa , 300, 300);

	// Turn on all contact notifications:
	gScene->setActorGroupPairFlags(0, 0, NX_NOTIFY_ON_TOUCH);

	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------

	
	mSoundManager->init();
	mSoundManager->setDistanceModel(AL_LINEAR_DISTANCE);
	//mSoundManager->setDopplerFactor(0.001f);

	/*
	mCamera->getParentSceneNode()->attachObject(mSoundManager->getListener());

	// Sound one - non streamed, looping, moving
	mSoundManager->createSound("One", "background.ogg", true, true, true);

	OgreOggISound* s = mSoundManager->getSound("One");
	s->setMaxDistance(1000);
	s->setReferenceDistance(0.5);
	s->setRolloffFactor(10);
	s->setRelativeToListener(true);

	//vehicleE->sceneNodeRoot->getAttachedObject(0)->getParentSceneNode()->attachObject(s);
		
	cubo1->getRaizSceneNode()->attachObject(s);

	s->play();

	/*
	// Sound two - prebuffered, streamed, looping, EFX room effect
	EAXREVERBPROPERTIES props = REVERB_PRESET_AUDITORIUM;
	mSoundManager->createSound("Two", "two.ogg", true, true, true);	
	mSoundManager->getSound("Two")->setMaxDistance(50);
	mSoundManager->getSound("Two")->setReferenceDistance(5);
	if ( mSoundManager->hasEFXSupport() )
	{
		mSoundManager->createEFXSlot();
		mSoundManager->createEFXEffect("Auditorium", AL_EFFECT_EAXREVERB, &props);
		mSoundManager->attachEffectToSound("Two", 0, "Auditorium");
	}
	mSoundManager->getSound("Two")->play();
	
	// Sound one - non streamed, looping, moving
	mSoundManager->createSound("Three", "three.ogg", true, true);
	mSoundManager->getSound("Three")->setMaxDistance(50);
	mSoundManager->getSound("Three")->setReferenceDistance(5);
	mOgreMonster->attachObject(mSoundManager->getSound("Three"));
	mSoundManager->getSound("Three")->play();
	
	// Sound one - streamed, looping, EFX Direct filter
	mSoundManager->createSound("background", "background.ogg", true, true, true);
	mSoundManager->getSound("background")->setRelativeToListener(true);
	mSoundManager->getSound("background")->setVolume(0.2f);
	mSoundManager->getSound("background")->setPriority(1);
	if ( mSoundManager->hasEFXSupport() )
	{
		mSoundManager->createEFXFilter("LowPassTest", AL_FILTER_LOWPASS, 0.1, 0.5);
		mSoundManager->attachFilterToSound("background", "LowPassTest");
	}
	//*/

	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
}

void CAplicacion::createCamera(void)
{
	/*
	mCamera = mSceneMgr->createCamera("CamaraPrincipal");
	mCamera->setPosition(Vector3(20,5,20));
	mCamera->lookAt(Vector3(0,0,0));
	mCamera->setNearClipDistance(0.1);
	*/

	//mCamera->setPolygonMode(PM_WIREFRAME);

	//---------------------------------------------------------------
	
	mCamera = mSceneMgr->createCamera("MainCamera"); 
	
	Ogre::SceneNode* camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode");
	Ogre::SceneNode* pitchNode = camNode->createChildSceneNode("PitchNode");
	pitchNode->attachObject(mCamera);	
	camNode->setPosition(0,0,0);
	
	//Ogre::Quaternion camPitchQuat = Ogre::Quaternion(Ogre::Degree(0),Ogre::Vector3(0,1,0));
	//camNode->getChild(0)->setOrientation(camPitchQuat);	
	
	mCamera->setProjectionType(Ogre::PT_PERSPECTIVE);	
	mCamera->setNearClipDistance(0.1f);
	mCamera->setFarClipDistance(100.0f);	
	mCamera->setPosition(Vector3(20,5,20));
	mCamera->lookAt(Vector3(0,0,0));
	
	mCamera->setAutoAspectRatio(true);

	//---------------------------------------------------------------
}

// Create new frame listener
void CAplicacion::createFrameListener(void)
{
    mFrameListener= new CFrameListener();
    mRoot->addFrameListener(mFrameListener);
}

CAplicacion * CAplicacion::getSingleton()
{
	return singleton;
}

void CAplicacion::CreateCube(const NxVec3& pos, int size, const NxVec3* initialVelocity, String material)
{
	struct _timeb timebuffer;
	char *timeline;

	_ftime( &timebuffer );
	timeline = ctime( & ( timebuffer.time ) );

	String id = CUtiles::int2str(timebuffer.time) + "_" +  CUtiles::int2str(rand()) + "_" + CUtiles::int2str(timebuffer.millitm);	
	
	//printf( "The time is %.19s.%hu %s", timeline, timebuffer.millitm, &timeline[20] );
	//cout << "ID " + id + "\n"; 

	CEntidad * cubo = new CEntidad(CUtiles::toVector3(pos));
    cubo->anadirFormaCubo
	(
		id,
		Corcho, 
		size,
		size,
		size,
		initialVelocity,
		material
	);
}

void CAplicacion::CreateCubeFromEye(int size)
{
	NxVec3 t = CUtiles::toNxVec3( mCamera->getPosition() );
	NxVec3 vel = CUtiles::toNxVec3( mCamera->getRealDirection() );
	vel.normalize();
	vel*=20.0f;
	CreateCube(t, size, &vel,
		"Examples/B" //"Examples/BumpyMetal"
		);

	//cout << "POS " + CUtiles::int2str(t.x) + CUtiles::int2str(t.y) + CUtiles::int2str(t.z) + "\n";
	//cout << "VEL " + CUtiles::int2str(vel.x) + CUtiles::int2str(vel.y) + CUtiles::int2str(vel.z) + "\n";
}

void CAplicacion::CreateStack(int size)
{
	const float cubeSize = 1.0f;
	const float spacing = -2.0f*gPhysicsSDK->getParameter(NX_SKIN_WIDTH);
	NxVec3 pos(0.0f, cubeSize, 0.0f);
	float offset = -size * (cubeSize * 2.0f + spacing) * 0.5f;
	while(size)
	{
		for(int i=0;i<size;i++)
		{
			pos.x = offset + (float)i * (cubeSize * 2.0f + spacing);
			CreateCube(pos, (int)cubeSize, NULL,
				"Examples/R" //"Examples/BumpyMetal"
				);
		}

		offset += cubeSize;
		pos.y += (cubeSize * 2.0f + spacing);
		size--;
	}
}

void CAplicacion::CreateTower(int size)
{
	const float cubeSize = 1.5f;
	const float spacing = 0.01f;
	NxVec3 pos(5.0f, cubeSize, 5.0f);
	while(size)
	{
		CreateCube(pos, (int)cubeSize, NULL,
			"Examples/Y" //"Examples/BumpyMetal"
			);
		pos.y += (cubeSize * 2.0f + spacing);
		size--;
	}
}
